package jmu.edu.yjc.service.neo4j.impl;

import jmu.edu.yjc.domain.ResponseResult;
import jmu.edu.yjc.domain.entity.neo4j.NodeEntity.*;
import jmu.edu.yjc.domain.entity.neo4j.RelationEntity.*;
import jmu.edu.yjc.domain.entity.neo4j.dto.DiseabledDto;
import jmu.edu.yjc.domain.entity.neo4j.vo.NodeVo;
import jmu.edu.yjc.domain.vo.PageVo;
import jmu.edu.yjc.domain.vo.common.EChartsVo;
import jmu.edu.yjc.domain.vo.common.TagVo;
import jmu.edu.yjc.enums.AppHttpCodeEnum;
import jmu.edu.yjc.exception.SystemException;
import jmu.edu.yjc.repository.DisableRepository;
import jmu.edu.yjc.service.answer.QuestionClassifier;
import jmu.edu.yjc.service.neo4j.DiseabledService;
import lombok.extern.slf4j.Slf4j;
import org.neo4j.driver.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;

@Service
@Slf4j
public class DiseabledServiceImpl implements DiseabledService ,AutoCloseable{
    @Resource
    private DisableRepository disableRepository;
    @Autowired
    private Driver driver;

    private static Set<String> incrDisease = new HashSet<>();

    private static volatile AtomicBoolean LOCK = new AtomicBoolean(false);
    @Override
    public ResponseResult getDiseabledList(Integer pageNum, Integer pageSize, String diseaseName) {
        log.info("查询所有疾病信息pageNumber:{},pageSize:{},diseaseName:{}",pageNum,pageSize,diseaseName);
        Session session = driver.session();
        Result result = null;
        Long total = 0L;
        List<Disease> diseases = new ArrayList<>();
        try {
            StringBuilder sb = new StringBuilder(1024);
            sb.append("match (n:disease) where 1=1 ");
            if(StringUtils.hasText(diseaseName)){
                sb.append("and n.name=~'.*").append(diseaseName).append(".*'");
            }
            String queryEnd = " return id(n) as id,n.cureWay as cureWay,n.curedProb as curedProb,n.cureLasttime as cureLasttime,n.easyGet as easyGet, n.getProb as getProb, n.name as name,n.cureDepartment as cureDepartment,n.desc as desc skip " + (pageNum-1)*pageSize + " limit " + pageSize;
            String countEnd = " return count(n) as total";
            total = session.run(sb.toString()+countEnd).next().get("total").asLong();
            result = session.run(sb.toString()+queryEnd);
            while (result.hasNext()){
                Record record = result.next();
                Disease disease = Disease.builder().name( record.get("name").asString())
                        .id(record.get("id").asLong()).cureDepartment(record.get("cureDepartment").asString())
                        .cureLasttime(record.get("cureLasttime").asString()).cureWay(record.get("cureWay").asString())
                        .curedProb(record.get("curedProb").asString()).easyGet(record.get("easyGet").asString())
                        .getProb(record.get("getProb").asString()).desc(record.get("desc").asString()).build();
                diseases.add(disease);
             }
        }catch (Exception e){
            e.printStackTrace();
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
        }finally {
            session.close();
        }
        return ResponseResult.okResult(new PageVo(diseases,total));
    }

    @Override
    public ResponseResult removeByIds(List<String> asList) {
        try {
            List<Long> ids = new ArrayList<>();
            for (String s : asList) {
                ids.add(Long.valueOf(s));
            }
            disableRepository.deleteAllById(ids);
        }catch (Exception e){
            e.printStackTrace();
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
        }
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult updateById(DiseabledDto  dto) {
        try {
            Disease disease = CoverDTOToDisease(dto);
            disableRepository.save(disease);
        }catch (Exception e){
            e.printStackTrace();
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
        }
        return ResponseResult.okResult();
    }
    public Disease CoverDTOToDisease(DiseabledDto dto){
        List<DoEat> doEat = new ArrayList<>();
        List<NoEat> noEat = new ArrayList<>();
        List<RecommendEat> recommendEat = new ArrayList<>();
        List<RecommendDrug> recommendDrug = new ArrayList<>();
        List<CommonDrug> commonDrug = new ArrayList<>();
        List<NeedCheck> needCheck = new ArrayList<>();
        List<HasSymptom> hasSymptom = new ArrayList<>();
        List<ComplicationWith> complicationWith = new ArrayList<>();
        List<BelongsTo> belongsTo = new ArrayList<>();
        dto.getDoEat().forEach(food -> doEat.add(DoEat.builder().name("宜吃").food(food).build()));
        dto.getNoEat().forEach(food -> noEat.add(NoEat.builder().name("忌吃").food(food).build()));
        dto.getRecommendEat().forEach(food -> recommendEat.add(RecommendEat.builder().name("推荐食物").food(food).build()));
        dto.getRecommendDrug().forEach(drug -> recommendDrug.add(RecommendDrug.builder().name("推荐用药").drug(drug).build()));
        dto.getCommonDrug().forEach(drug -> commonDrug.add(CommonDrug.builder().name("常规用药").drug(drug).build()));
        dto.getNeedCheck().forEach(check -> needCheck.add(NeedCheck.builder().name("需检查项目").check(check).build()));
        dto.getHasSymptom().forEach(symptom -> hasSymptom.add(HasSymptom.builder().name("症状").symptom(symptom).build()));
        dto.getComplicationWith().forEach(acompany -> complicationWith.add(ComplicationWith.builder().name("并发症").acompany(acompany).build()));
        dto.getBelongsTo().forEach(department -> belongsTo.add(BelongsTo.builder().name("属于").department(department).build()));
        return Disease.builder().id(dto.getId())
                .prevent(dto.getPrevent()).getProb(dto.getGetProb())
                .easyGet(dto.getEasyGet()).curedProb(dto.getCuredProb())
                .cureWay(dto.getCureWay()).cureLasttime(dto.getCureLasttime())
                .cureDepartment(dto.getCureDepartment()).cause(dto.getCause())
                .name(dto.getName()).desc(dto.getDesc()).doEat(doEat).noEat(noEat)
                .belongsTo(belongsTo).complicationWith(complicationWith).recommendDrug(recommendDrug)
                .recommendEat(recommendEat).commonDrug(commonDrug).needCheck(needCheck).hasSymptom(hasSymptom).build();
    }
    @Override
    public ResponseResult getById(Long id) {
        Optional<Disease> disease = disableRepository.findById(id);
        if(disease.isPresent()){
            return ResponseResult.okResult(disease.get());
        }else {
            return  ResponseResult.errorResult(-1,"未找到节点信息");
        }
    }

    @Override
    public ResponseResult searchNodeByName(String relation, String type, String query) {
        log.info("查询相关节点信息node:{},relation:{},query:{}",type,relation,query);
        Session session = driver.session();
        Result result = null;
        List<NodeVo> nodeVos = new ArrayList<>();
        try{
            StringBuilder sb = new StringBuilder(1024);
            sb.append("match(n:").append(type).append(") where n.name=~'.*")
                    .append(query).append(".*' return n.name as name,id(n) as id limit 50");
            result = session.run(sb.toString());
            while (result.hasNext()) {
                Record record = result.next();
                NodeVo vo = NodeVo.builder().id(record.get("id").asLong())
                        .name(record.get("name").asString()).build();
                nodeVos.add(vo);
            }
        }catch (Exception e){
            e.printStackTrace();
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
        }finally {
            session.close();
        }
        return ResponseResult.okResult(nodeVos);
    }
    @Transactional
    @Override
    public ResponseResult saveDiseabled(DiseabledDto dto) throws IOException {
        if(disableRepository.existsByName(dto.getName())){
            return ResponseResult.errorResult(-1,"疾病已存在");
        }
        ClassPathResource pathResource = new ClassPathResource("static/dict/disease.txt");
        BufferedWriter out = null;

        Disease disease = CoverDTOToDisease(dto);
        disableRepository.save(disease);

        out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(pathResource.getFile(),true)));
        out.write("\n" + dto.getName());
        out.flush();
        incrDisease.add(dto.getName());
        out.close();

        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult updateCache() {
        if(!LOCK.compareAndSet(false,true)){
            return ResponseResult.errorResult(-1,"同步任务进行中请稍等");
        }
        try {
            if(incrDisease.size()>0){
                QuestionClassifier.IncrementalBuild(incrDisease,"disease");
            }
        }finally {
            LOCK.set(false);
        }
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult getTotal() {
        return ResponseResult.okResult(disableRepository.count());
    }

    @Override
    public ResponseResult randomDisable(Integer total) {
        Session session = driver.session();
        Result result = null;
        List<TagVo> tags = new ArrayList<>();
        Random random = new Random();
        int nextInt = random.nextInt(total - 20);
        try {
            String query = "match(n:disease) return n.name as name skip "+ nextInt +" limit 20";
            result = session.run(query);
            int i = 0;
            while (result.hasNext()) {
                Record record = result.next();
                TagVo tagVo = new TagVo(i,record.get("name").asString());
                tags.add(tagVo);
                i++;
            }
        }catch (Exception e){
            e.printStackTrace();
            return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR);
        }finally {
            session.close();
        }
        return ResponseResult.okResult(new EChartsVo(tags,"疾病标签"));
    }

    @Override
    public void close() throws Exception {
        driver.close();
    }
}
